
/* GCSx
** EVENT.CPP
**
** Our custom event queue to allow preempting SDL's event queue
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#include "all.h"

// We need enough events to handle changing every layer of a scene
// at once, with room to spare; game event queue doesn't need as
// much room but we keep it safe
#define MAX_EVENTS  128

SDL_Event eventQueue[MAX_EVENTS];
int eventHead = 0;
int eventTail = 0;

SDL_Event gameEventQueue[MAX_EVENTS];
int gameEventHead = 0;
int gameEventTail = 0;

void insertEvent(const SDL_Event* customEvent) { start_func
    int tail = (eventTail + 1) % MAX_EVENTS;

    // If we're full, drop the event
    // This should never occur in normal usage, and
    // even if it does should be harmless as these are
    // only CLOSE, FOCUS, and OBJCHANGE events
    if (tail == eventHead) {
/* SDL_OBJECTCHANGE events no longer use the queue
**
        if (customEvent->type == SDL_OBJECTCHANGE) {
            delete (ObjChange*)customEvent->user.data1;
        }
**
*/
        return;
    }
    
    eventQueue[eventTail] = *customEvent;
    eventTail = tail;
}

void preemptEvent(const SDL_Event* customEvent) { start_func
    int head = (eventHead + MAX_EVENTS - 1) % MAX_EVENTS;

    // If we're full, drop the event
    // This should never occur in normal usage, and
    // even if it does should be harmless as these are
    // only CLOSE, FOCUS, and OBJCHANGE events
    if (head == eventTail) {
/* SDL_OBJECTCHANGE events no longer use the queue
**
        if (customEvent->type == SDL_OBJECTCHANGE) {
            delete (ObjChange*)customEvent->user.data1;
        }
**
*/
        return;
    }
    
    eventHead = head;
    eventQueue[eventHead] = *customEvent;
}

/* SDL_OBJECTCHANGE events no longer use the queue
**
void combinatoryEvent(const SDL_Event* customEvent) { start_func
    // Right now, we only know how to combine SDL_OBJCHANGE events
    assert(customEvent->type == SDL_OBJECTCHANGE);

    // Scan all existing events
    for (int pos = eventHead; pos != eventTail; pos = (pos + 1) % MAX_EVENTS) {
        // Matching type and code
        if ((eventQueue[pos].type == customEvent->type) && 
            (eventQueue[pos].user.code == customEvent->user.code)) {
            // We only support one type of combination right now
            if ((customEvent->user.code & (OBJMOD_TILE | OBJMOD_COLL)) &&
                (customEvent->user.code & OBJ_TILESET)) {
                // (for now, these must be SDL_OBJCHANGE events)
                const ObjChange* obj1 = (ObjChange*)customEvent->user.data1;
                ObjChange* obj2 = (ObjChange*)eventQueue[pos].user.data1;
                
                // Replace old event with min/max of combined events
                if (obj1->info1 < obj2->info1) obj2->info1 = obj1->info1;
                if (obj1->info2 > obj2->info2) obj2->info2 = obj1->info2;
                
                // Delete old event details
                delete obj1;
                return;
            }
        }
    }
    
    // No combination made
    preemptEvent(customEvent);
}
**
*/

int eventsWaiting() { start_func
    if (eventTail == eventHead) return 0;
    return 1;
}

int grabEvent(SDL_Event* store) { start_func
    if (eventTail == eventHead) {
        if (SDL_PollEvent(store)) return 1;
        return 0;
    }
    *store = eventQueue[eventHead];
    eventHead = (eventHead + 1) % MAX_EVENTS;
    return 1;
}

void cleanEvents(const void* window) { start_func
    for (int pos = eventHead; pos != eventTail; pos = (pos + 1) % MAX_EVENTS) {
        if (eventQueue[pos].user.data1 == window) eventQueue[pos].type = SDL_NOEVENT;
    }
}

void clearEvents() { start_func
/* SDL_OBJECTCHANGE events no longer use the queue
**
    // Clear our events only
    while (eventTail != eventHead) {
        if (eventQueue[eventHead].type == SDL_OBJECTCHANGE) {
            delete (ObjChange*)eventQueue[eventHead].user.data1;
        }
        eventHead = (eventHead + 1) % MAX_EVENTS;
    }
**
*/

    eventTail = eventHead;
    gameEventTail = gameEventHead;
}

void storeEventGame(const SDL_Event* event) { start_func
    int tail = (gameEventTail + 1) % MAX_EVENTS;

    // If we're full, drop the event
    if (tail == gameEventHead) return;

    gameEventQueue[gameEventTail] = *event;
    gameEventTail = tail;
}

int grabEventGame(SDL_Event* store) { start_func
    if (gameEventTail == gameEventHead) return 0;
    *store = gameEventQueue[gameEventHead];
    gameEventHead = (gameEventHead + 1) % MAX_EVENTS;
    return 1;
}
